package cricket

import (
	"encoding/json"
	"errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	global2 "longmen/server/config/global"
	"longmen/server/internal/services/nami/config"
	"longmen/server/internal/services/nami/services"
	"longmen/server/pkg/common/helper/encrypt"
	"longmen/server/pkg/db/mysql/models/models_sports"
	"time"
)

type InsertMatchInterface interface {
	Get() models_sports.CricketMatch
}

type UpdateMatchInterface interface {
	GetUpdate() models_sports.CricketMatch
}

var (
	updateMatchField = []string{
		"id", "unique_tournament_id", "season_id", "tournament_id",
		"venue_id", "home_team_id", "away_team_id",
		"match_time", "status_id", "neutral", "mlive", "lineup",
		"scores", "extra_scores", "weather",
	}

	matchTempField = []string{
		"id", "match_id", "match_time", "sport_id", "comp",
		"comp_short_zh", "comp_logo", "home", "home_logo", "away",
		"away_logo", "home_score", "away_score",
		"match_status", "match_detail_status", "raw_match_status",
	}
)

func CrateMatch(a []InsertMatchInterface) {
	records := make([]models_sports.CricketMatch, 0)
	for _, c := range a {
		records = append(records, c.Get())
	}
	if len(records) == 0 {
		return
	}
	clauses := clause.OnConflict{
		Columns: []clause.Column{{Name: "id"}},
		//"time_played", 进行中的时间不更新
		DoUpdates: clause.AssignmentColumns(updateMatchField),
	}
	err := global2.Sport().Model(records).Clauses(clauses).CreateInBatches(records, 100).Error
	if err != nil {
		return
	}
	batchToMatchTempSchedule(transTo(records))

}

// for match create.
func transTo(records []models_sports.CricketMatch) []models_sports.MatchTempSchedule {
	data := make([]models_sports.MatchTempSchedule, 0)
	var compIds []int
	var teamIds []int
	for _, r := range records {
		compIds = append(compIds, r.UniqueTournamentID)
		teamIds = append(teamIds, r.HomeTeamID)
		teamIds = append(teamIds, r.AwayTeamID)
		// 转换成数组运算
	}

	teamMap, _ := GetTeamIdMaps(teamIds)
	competitionMap, _ := GetUniqueTournamentIdMaps(compIds)
	for _, c := range records {
		var scores map[string][]int
		err := json.Unmarshal([]byte(c.Scores), &scores)
		if err != nil {
			global2.Log.Errorf("解析json失败: %v", err.Error())
			return data
		}
		comp, ok := competitionMap[c.UniqueTournamentID]
		if !ok {
			// 空数据没匹配到
			comp = models_sports.CricketUniqueTournament{}
		}

		ftScores, ok := scores["ft"]
		if !ok {
			// 空数据没匹配到
			comp = models_sports.CricketUniqueTournament{}
			// WARN: 有些 比赛 没有 ft 比分, 此处 补上.
			ftScores = []int{0, 0}
		}

		found := true
		hTeam, ok := teamMap[c.HomeTeamID]
		if !ok {
			global2.Log.Infof("cricket team id:%v,not found", c.AwayTeamID)
			// 空数据没匹配到
			hTeam = models_sports.CricketTeam{}
			found = false
		}
		aTeam, ok := teamMap[c.AwayTeamID]
		if !ok {
			global2.Log.Infof("cricket team id:%v,not found", c.AwayTeamID)
			// 空数据没匹配到
			aTeam = models_sports.CricketTeam{}
			found = false
		}
		if !found {
			continue
		}
		if aTeam.ShortNameEn == "" {
			aTeam.ShortNameEn = aTeam.NameEn
		}
		if hTeam.ShortNameEn == "" {
			hTeam.ShortNameEn = hTeam.NameEn
		}
		if comp.ShortNameEn == "" {
			comp.ShortNameEn = comp.NameEn
		}
		if comp.ShortNameZh == "" {
			comp.ShortNameZh = comp.NameZh
		}
		if hTeam.ShortNameZh == "" {
			hTeam.ShortNameZh = hTeam.NameZh
		}
		if aTeam.ShortNameZh == "" {
			aTeam.ShortNameZh = aTeam.NameZh
		}
		temp := models_sports.MatchTempSchedule{
			MatchId:           c.ID,
			MatchTime:         c.MatchTime,
			SportId:           config.Cricket,
			Comp:              comp.NameZh,
			CompShortZh:       comp.ShortNameZh,
			CompShortEn:       comp.ShortNameEn,
			CompLogo:          comp.Logo,
			Home:              hTeam.ShortNameZh,
			HomeLogo:          hTeam.Logo,
			Away:              aTeam.ShortNameZh,
			AwayLogo:          aTeam.Logo,
			HomeScore:         uint32(ftScores[0]),
			AwayScore:         uint32(ftScores[1]),
			MatchStatus:       c.StatusID.MatchTempScheduleStatus(), // 转换为标准 状态,
			RawMatchStatus:    uint32(c.StatusID),
			MatchDetailStatus: c.StatusID.ToString(),
			AwayEn:            aTeam.ShortNameEn,
			HomeEn:            hTeam.ShortNameEn,
		}
		data = append(data, temp)
	}
	return data
}

// for match update - partly.
func transToForUpdatePartly(records []models_sports.CricketMatch) []models_sports.MatchTempSchedule {
	// tip: no need query teams when update match,
	data := make([]models_sports.MatchTempSchedule, 0)

	for _, c := range records {
		var scores map[string][]int
		err := json.Unmarshal([]byte(c.Scores), &scores)
		if err != nil {
			global2.Log.Errorf("解析json失败: %v", err.Error())
			return data
		}

		ftScores, ok := scores["ft"]
		if !ok {
			// WARN: 有些 比赛 没有 ft 比分, 此处 补上.
			ftScores = []int{0, 0}
		}

		temp := models_sports.MatchTempSchedule{
			MatchId:           c.ID,
			MatchTime:         c.MatchTime,
			SportId:           config.Cricket,
			HomeScore:         uint32(ftScores[0]),
			AwayScore:         uint32(ftScores[1]),
			MatchStatus:       c.StatusID.MatchTempScheduleStatus(), // 转换为标准 状态,
			RawMatchStatus:    uint32(c.StatusID),
			MatchDetailStatus: c.StatusID.ToString(),
		}
		data = append(data, temp)
	}
	return data
}

func batchToMatchTempSchedule(records []models_sports.MatchTempSchedule) {
	if len(records) == 0 {
		return
	}
	clauses := clause.OnConflict{
		Columns: []clause.Column{{Name: "match_id"}},
		//"time_played", 进行中的时间不更新
		// "flag", 自定义FLAG
		DoUpdates: clause.AssignmentColumns(matchTempField),
	}
	err := global2.Sport().Model(records).Clauses(clauses).CreateInBatches(records, 100).Error
	if err != nil {
		return
	}
}

func GetMatchTime() int {
	var data models_sports.CricketMatch
	err := global2.Sport().Model(&data).Order("updated_at DESC").Limit(1).Take(&data).Error
	if err != nil {
		return 0
	}
	return data.UpdatedAt
}

func GetMatchId() int {
	var data models_sports.CricketMatch
	err := global2.Sport().Model(&data).Order("id DESC").Limit(1).Take(&data).Error
	if err != nil {
		return 0
	}
	return data.ID
}

func DeleteMatch(ids []int) {
	if len(ids) == 0 {
		return
	}
	var model models_sports.CricketMatch
	err := global2.Sport().Where("id in ?", ids).Delete(&model).Error
	if err != nil {
		global2.Log.Error("删除赛程失败：", err, "ids: ", ids)
		return
	}
	services.DeleteMatchSchedule(ids, config.Cricket)
}

func GetByExists(update models_sports.CricketMatch) bool {
	marshal, _ := json.Marshal(update)
	md5 := encrypt.Md5(string(marshal))
	nx := global2.GetRds().SetNX("cricket_update:"+md5, "", time.Second*300)
	if !nx.Val() {
		//global.Log.Error("该记录命中缓存，无需修改：", string(marshal))
		return true
	}
	return false
}

func UpdateMatchScore(a []UpdateMatchInterface) {
	records := make([]models_sports.CricketMatch, 0)
	for _, c := range a {
		update := c.GetUpdate()
		if GetByExists(update) {
			continue
		}
		records = append(records, update)
	}
	if len(records) == 0 {
		return
	}
	// 目前只更新这几个字段
	updateFields := []string{
		"id", "status_id", "scores", "extra_scores",
	}
	for _, record := range records {
		err := global2.Sport().Transaction(func(tx *gorm.DB) error {
			match := models_sports.CricketMatch{}
			err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
				Where("id", record.ID).
				Limit(1).
				Find(&match).
				Error

			if err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return nil
				}
				return err
			}
			err = tx.Model(record).Select(updateFields).Updates(record).Error
			if err != nil {
				return err
			}
			return nil
		})
		if err != nil {
			global2.Log.Info("板球更新失败: ", err)
			continue
		}

	}
	batchUpdateToMatchTempScheduleScore(transToForUpdatePartly(records))
}

func batchUpdateToMatchTempScheduleScore(records []models_sports.MatchTempSchedule) {
	updateFields := []string{
		"match_id", "match_status", "match_detail_status", "home_score", "away_score", "raw_match_status",
	}
	for _, record := range records {
		err := global2.Sport().Model(record).Select(updateFields).
			Where("match_id = ? and sport_id = ?", record.MatchId, config.Cricket).
			Updates(record).Error
		if err != nil {
			return
		}
	}
}

func GetLiveUpMatchIds(startTime, endTime int64, lastId int) []int {
	//model := models_sports.CricketMatch{}
	records := make([]models_sports.CricketMatch, 0)
	//"match_time > unix_timestamp( '2022-06-27 00:00:00' ) AND match_time < unix_timestamp( '2022-06-28 00:00:00' )"
	ids := make([]int, 0)
	err := global2.Sport().Where("lineup = 1").
		Where("match_time >= ?  AND match_time <= ?", startTime, endTime).
		Where("id > ?", lastId).
		//Where("status_id in ?", model.GetStartingStatus()).
		Order("id asc").
		Limit(120).
		Find(&records).Error
	if err != nil {
		return ids
	}
	for _, record := range records {
		ids = append(ids, record.ID)
	}
	return ids
}
